package com.yuxiang.flowable.util;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.Bucket;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectResult;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author : yx-0176
 * @description
 * @date : 2022/3/9
 */
@Slf4j
public class Oss {

    private static String ENDPOINT;

    private static String ACCESS_KEY_ID;

    private static String ACCESS_KEY_SECRET;

    /**
     *  oss.endpoint
     *  oss.accessKeyId
     *  oss.accessKeySecret
     *  oss.bucketName
     *  oss.protocolName
     *  oss.expireTimeDayCount
     *  oss.previewUrl
     */

    static {
        ResourceBundle bundle = PropertyResourceBundle.getBundle("ladder");
        ENDPOINT = bundle.containsKey("oss.endpoint") == false ? "" : bundle.getString("oss.endpoint");
        ACCESS_KEY_ID = bundle.containsKey("oss.accessKeyId") == false ? "" : bundle.getString("oss.accessKeyId");
        ACCESS_KEY_SECRET = bundle.containsKey("oss.accessKeySecret") == false ? "" : bundle.getString("oss.accessKeySecret");
    }


    private static final OSS getOSSClient() {
        return new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
    }


    public static final boolean createBucket(String bucketName) {
        Bucket bucket = getOSSClient().createBucket(bucketName);
        return bucketName.equals(bucket.getName());
    }


    public static final void deleteBBucket(String bucketName) {
        getOSSClient().deleteBucket(bucketName);
        log.info("delete" + bucketName + "Bucket success!");
    }

    /**
     * 上传 InputStream 到 OSS 服务器
     *
     * @param inputStream
     * @param fileName
     * @param fileSize
     * @param bucketName
     * @param diskName
     * @return
     */
    public static final String uploadObject2OSS(InputStream inputStream, String fileName, Long fileSize,
                                                String bucketName, String diskName) {

        // 获取 ossClient
        OSSClient ossClient = (OSSClient) getOSSClient();


        try {
            // 生成 OSS 中 Object 的元数据
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(inputStream.available());
            metadata.setCacheControl("no-cache");
            metadata.setHeader("Pragma", "no-cache");
            metadata.setContentEncoding("utf-8");
            metadata.setContentType(getContentType(fileName));
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");

            // 上传文件
            PutObjectResult putObjectResult = ossClient.putObject(bucketName, diskName + fileName, inputStream, metadata);

            // 解析结果
            return putObjectResult.getETag();
        } catch (Exception e) {
            log.error("upload InputStream to OSS exception : " + e.getMessage(), e);
            throw new RuntimeException("Fail to upload InputStream");
        } finally {
            ossClient.shutdown();
        }

    }


    /**
     * 单个文件下载
     *
     * @param bucketName
     * @param key
     * @return
     * @throws Throwable
     */
    public static final void downloadObject(HttpServletRequest request, HttpServletResponse response,
                                            String bucketName, String key, String fileName) throws IOException {
        OSSClient ossClient = (OSSClient) getOSSClient();
        BufferedInputStream buff = null;
        BufferedOutputStream out = null;
        try {
            OSSObject ossObject = ossClient.getObject(bucketName, key);


            String header = request.getHeader("User-Agent").toUpperCase();
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                fileName = URLEncoder.encode(fileName, "utf-8");
                //IE下载文件名空格变+号问题
                fileName = fileName.replace("+", "%20");
            } else {
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            }
            response.reset();
            response.setContentType("text/plain");
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Location", fileName);
            response.setHeader("Cache-Control", "max-age=0");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);


            buff = new BufferedInputStream(ossObject.getObjectContent());
            out = new BufferedOutputStream(response.getOutputStream());
            byte[] car = new byte[1024];

            int i = buff.read(car);
            while (i != -1) {
                out.write(car, 0, i);
                i = buff.read(car);
            }
            // 关闭流

        } catch (Exception e) {
            log.error("download InputStream to OSS exception : " + e.getMessage(), e);
            throw new RuntimeException("Fail to download InputStream");
        } finally {
            if (Objects.nonNull(buff)) {
                buff.close();
            }
            if (Objects.nonNull(out)) {
                out.close();
            }
            ossClient.shutdown();
        }

    }


    /**
     * 下载多个文件到zip
     *
     * @param bucketName
     * @param keyList
     * @param request
     * @param response
     */
    public static void downloadMultiObject2Zip(String bucketName, List<Map<String, String>> keyList, HttpServletRequest request,
                                               HttpServletResponse response) throws IOException {
        String fileName = "简历.zip";

        //临时文件名称
        File zipFile = File.createTempFile("test", ".zip");
        ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
        OSSClient ossClient = (OSSClient) getOSSClient();
        for (Map<String, String> map : keyList) {

            // 获取Object，返回结果为OSSObject对象
            OSSObject ossObject = ossClient.getObject(bucketName, map.get("key"));
            // 读去Object内容  返回
            InputStream inputStream = ossObject.getObjectContent();

            // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名
            zos.putNextEntry(new ZipEntry(map.get("childFileName") + ".pdf"));
            int bytesRead = 0;
            // 向压缩文件中输出数据
            while ((bytesRead = inputStream.read()) != -1) {
                zos.write(bytesRead);
            }
            inputStream.close();
            zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
        }
        String header = request.getHeader("User-Agent").toUpperCase();
        if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
            fileName = URLEncoder.encode(fileName, "utf-8");
            //IE下载文件名空格变+号问题
            fileName = fileName.replace("+", "%20");
        } else {
            fileName = new String(fileName.getBytes(), "ISO8859-1");
        }
        response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName));
        response.setContentType("application/x-msdownload");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        try (FileInputStream fis = new FileInputStream(zipFile); BufferedInputStream buff = new BufferedInputStream(fis);
             BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream())) {
            byte[] car = new byte[1024];
            int l = 0;
            while (l < zipFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                out.write(car, 0, j);
            }
        } catch (IOException e) {
            log.info(e.getMessage());
        }
        ossClient.shutdown();
        zos.close();
        zipFile.delete();
    }

    /**
     * 下载多个文件到zip
     *
     * @param bucketName
     * @param keyList
     * @param request
     * @param response
     * @param fileName   下载后的文件名
     */
    public static final void downloadMultiObject2Zip(String bucketName, List<Map<String, String>> keyList, HttpServletRequest request,
                                                     HttpServletResponse response, String fileName) throws IOException {

        // 创建临时文件
        File zipFile = null;
        ZipOutputStream zos = null;
        try {
            //临时文件名称
            zipFile = File.createTempFile("test", ".zip");

            FileOutputStream f = new FileOutputStream(zipFile);
            /**
             * 作用是为任何OutputStream产生校验和
             * 第一个参数是制定产生校验和的输出流，第二个参数是指定Checksum的类型 （Adler32（较快）和CRC32两种）
             */
            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            // 用于将数据压缩成Zip文件格式
            zos = new ZipOutputStream(csum);
            OSSClient ossClient = (OSSClient) getOSSClient();
            for (Map<String, String> map : keyList) {

                // 获取Object，返回结果为OSSObject对象
                OSSObject ossObject = ossClient.getObject(bucketName, map.get("key"));
                // 读去Object内容  返回
                InputStream inputStream = ossObject.getObjectContent();

                // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名
                zos.putNextEntry(new ZipEntry(map.get("childFileName") + ".pdf"));
                int bytesRead = 0;
                // 向压缩文件中输出数据
                while ((bytesRead = inputStream.read()) != -1) {
                    zos.write(bytesRead);
                }
                inputStream.close();
                zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
            }
            ossClient.shutdown();
            zos.finish();
        } catch (IOException e) {
            log.info(e.getMessage());
        } finally {
            //保证 zos流关闭在out流之前  否则会导致winRAR 打不开
            if (Objects.nonNull(zos)) {
                zos.close();
            }
        }
        String header = request.getHeader("User-Agent").toUpperCase();
        if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
            fileName = URLEncoder.encode(fileName, "utf-8");
            //IE下载文件名空格变+号问题
            fileName = fileName.replace("+", "%20");
        } else {
            fileName = new String(fileName.getBytes(), "ISO8859-1");
        }
        response.reset();
        response.setContentType("text/plain");
        response.setContentType("application/octet-stream; charset=utf-8");
        response.setHeader("Location", fileName);
        response.setHeader("Cache-Control", "max-age=0");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

        try (FileInputStream fis = new FileInputStream(zipFile); BufferedInputStream buff = new BufferedInputStream(fis);
             BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream())) {
            byte[] car = new byte[1024];
            int l = 0;
            while (l < zipFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                out.write(car, 0, j);
            }
        } catch (IOException e) {
            log.info(e.getMessage());
        } finally {
            // 删除临时文件
            if (Objects.nonNull(zipFile)) {
                zipFile.delete();
            }
        }

    }


    public static final String getContentType(String fileName) {
        String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1);

        if ("bmp".equalsIgnoreCase(fileExtension)) {
            return "image/bmp";
        }

        if ("gif".equalsIgnoreCase(fileExtension)) {
            return "image/gif";
        }

        if ("jpeg".equalsIgnoreCase(fileExtension) || "jpg".equalsIgnoreCase(fileExtension)
                || "png".equalsIgnoreCase(fileExtension)) {
            return "image/jpeg";
        }

        if ("html".equalsIgnoreCase(fileExtension)) {
            return "text/html";
        }

        if ("txt".equalsIgnoreCase(fileExtension)) {
            return "text/plain";
        }

        if ("vsd".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.visio";
        }

        if ("ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.ms-powerpoint";
        }

        if ("doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
            return "application/msword";
        }

        if ("xml".equalsIgnoreCase(fileExtension)) {
            return "text/xml";
        }

        if ("pdf".equalsIgnoreCase(fileExtension)) {
            return "application/pdf";
        }
        if ("zip".equalsIgnoreCase(fileExtension)) {
            return "application/zip";
        }

        return "text/html";
    }


}
